// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: linchuan

#pragma once

#include <algorithm>
#include <cstdio>
#include <memory>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

#include <nlohmann/json.hpp>
#include <rapidjson/document.h>

#include "coin2/exchange/base/order/order.h"
#include "coin2/exchange/prex_futures/symbology/currency.h"
#include "coin2/exchange/prex_futures/symbology/product.h"

namespace coin2::exchange::prex_futures::order_v1 {
using coin::proto::ProductOrderElement;
class PrexFuturesParser : public base::order::BaseParser {
 public:
  using PrexFuturesProductInfoCache = coin2::exchange::base::order::ProductInfoCache<
      coin2::exchange::prex_futures::symbology::PrexFuturesProduct,
      coin2::exchange::prex_futures::symbology::PrexFuturesCurrency>;

 public:
  PrexFuturesParser() = default;
  const ParseResult& ParseRawMessage(const RawMessage& msg) override;
  AccountBalance ParseBalance(const JsonValue& doc) override;
  AccountPosition ParsePosition(const JsonValue& doc) override;
  nlohmann::json ParseHeader(const std::shared_ptr<AsioHttpContext>&);
  bool ParseRawBalance(const JsonValue& doc, RawBalanceMap* raw_balance_map);
  bool ParseRawPosition(const JsonValue& doc, RawPositionMap* raw_position_map);

 private:
  void ParseSubmitResponse();
  void ParseCancelResponse();
  void ParseMultiCancelResponse();
  void ParseAmendResponse();
  void ParseConversionResponse();
  void ParseRestOpenOrders();
  void ParseRestAllOrders();
  void ParseRestFillOrders();
  void ParseRestAccountBalance();
  void ParseRestAccountPosition();
  void ParseRestHistConversions();
  void ParseWsMessage();
  void ParseWsAccountBalance(const JsonValue& document);
  void ParseWsAccountPosition(const JsonValue& document);

  void ParseWallet(const JsonValue& doc);
  bool ParseMargin(const JsonValue& doc, RawBalanceMap* raw_balance_map);

  std::string ParseAuthToken(const JsonValue& doc);
  void ParseWsWallet(const JsonValue& doc);
  bool ParseWsMargin(const JsonValue& doc, RawBalanceMap* raw_balance_map);
  AccountBalance ParseWsBalance(const JsonValue& doc);
  AccountPosition ParseWsPosition(const JsonValue& doc);
  bool ParseRawPositionFromWs(const JsonValue& doc, RawPositionMap* raw_position_map);
  ProductOrderBundle ParseWsOrderInfoListV2(const JsonValue& data);
  ProductOrderBundle ParseWsOrderInfoListV3(const JsonValue& data);
  ProductFillBundle ParseWsFillInfoListV2(const JsonValue& data);
  std::tuple<bool, ProductOrderElement> ParseOrderInfo(const JsonValue& data);
  coin::proto::ProductFillElement ParseFillInfo(const JsonValue& data);
  coin::proto::ConversionElement ParseConversion(const JsonValue& data);
  bool LoadZombieOrderCheckList(const std::string& mixin_order_ids);
  void CheckZombieOrder(ProductOrderBundle* product_order_bundle);

 private:
  std::unordered_map<std::string, double> margin_cache_map_;
  std::unordered_map<std::string, CurrencyBalance> balance_cache_map_;
  std::unordered_map<std::string, std::vector<int64_t>> mixin_zombie_order_ids_map_;
  std::unordered_set<std::string> conversion_ids_cache_;
  bool is_wallet_ = false;
  bool is_margin_ = false;
  bool is_account_wallet_initialized_ = false;
  bool is_account_balance_initialized_ = false;
  bool is_account_position_initialized_ = false;
  bool is_first_query_hist_conversions_ = true;
};

}  // namespace coin2::exchange::prex_futures::order_v1
